package com.boarsoft.boar.app.biz;

import java.util.List;

import com.boarsoft.boar.entity.AppZoneSlot;
import org.apache.commons.collections.CollectionUtils;
import org.hibernate.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.boarsoft.boar.app.AppInstBiz;
import com.boarsoft.boar.entity.AppInfo;
import com.boarsoft.boar.entity.AppInst;
import com.boarsoft.boar.entity.ServerInfo;
import com.boarsoft.boar.server.ServerBiz;
import com.boarsoft.common.Util;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.hibernate.biz.SimpleBizImpl;

@Component
public class AppInstBizImpl extends SimpleBizImpl implements AppInstBiz {

	@Autowired
	protected ServerBiz serverBiz;

	@Override
	@Transactional(readOnly = true)
	public PagedResult<AppInst> list(String env, String serverId, String appId, String projId, String group, Short status,
			String key, String orderBy, int pageNo, int pageSize) {
		StringBuilder wqb = new StringBuilder();
		wqb.append(" where 1=1");
		if (Util.strIsNotEmpty(env) && !"ALL".equalsIgnoreCase(env)) {
			wqb.append(" and s.env='").append(env).append("'");
		}
		if (Util.strIsNotEmpty(serverId)) {
			wqb.append(" and i.serverId='").append(serverId).append("'");
		}
		if (Util.strIsNotEmpty(appId)) {
			wqb.append(" and i.appId='").append(appId).append("'");
		}
		if (Util.strIsNotEmpty(projId)) {
			wqb.append(" and i.projId='").append(projId).append("'");
		}
		if (Util.strIsNotEmpty(group)) {
			wqb.append(" and i.group='").append(group).append("'");
		}
		if (status != null) {
			wqb.append(" and i.status=").append(status);
		}
		if (Util.strIsNotEmpty(key)) {
			wqb.append(" and (s.code like '%").append(key);
			wqb.append("%' or s.name like '%").append(key);
			wqb.append("%' or s.ip like '%").append(key);
			wqb.append("%')");
		}
		StringBuilder cqb = new StringBuilder();
		cqb.append("select count(i.id) from ").append(AppInst.class.getName());
		cqb.append(" i join i.app a join i.server s").append(wqb.toString());
		int total = dao.getTotal(cqb.toString());
		//
		StringBuilder lqb = new StringBuilder();
		lqb.append("select i from ").append(AppInst.class.getName());
		lqb.append(" i join fetch i.app a join fetch i.server s").append(wqb.toString());
		if (Util.strIsNotEmpty(orderBy)) {
			lqb.append(" order by i.").append(orderBy);
		} else {
			lqb.append(" order by i.ip");
		}
		List<AppInst> lt = dao.list(lqb.toString(), AppInst.class, pageNo, pageSize);
		for (AppInst i : lt) {
			dao.evict(i);
			dao.evict(i.getApp());
			dao.evict(i.getServer());
		}
		return new PagedResult<AppInst>(total, lt, pageNo, pageSize);
	}

	@Override
	@Transactional(readOnly = true)
	public List<AppInst> list(String env, String appId, String group, Short status) {
		StringBuilder wqb = new StringBuilder();
		wqb.append(" where 1=1");
		if (Util.strIsNotEmpty(env) && !"ALL".equalsIgnoreCase(env)) {
			wqb.append(" and s.env='").append(env).append("'");
		}
		if (Util.strIsNotEmpty(appId)) {
			wqb.append(" and i.appId='").append(appId).append("'");
		}
		if (Util.strIsNotEmpty(group)) {
			wqb.append(" and i.group='").append(group).append("'");
		}
		if (status != null) {
			wqb.append(" and i.status=").append(status);
		}
		// StringBuilder cqb = new StringBuilder();
		// cqb.append("select count(i.id) from
		// ").append(AppInst.class.getName());
		// cqb.append(" i join i.app a join i.server s").append(wqb.toString());
		// int total = dao.getTotal(cqb.toString());
		//
		StringBuilder lqb = new StringBuilder();
		lqb.append("select i from ").append(AppInst.class.getName());
		lqb.append(" i join fetch i.app a join fetch i.server s").append(wqb.toString());
		List<AppInst> lt = dao.list(lqb.toString(), AppInst.class);
		for (AppInst i : lt) {
			dao.evict(i);
			dao.evict(i.getApp());
			dao.evict(i.getServer());
		}
		return lt;
	}

	@Override
	@Transactional
	public int add(AppInst o) {
		StringBuilder sb = new StringBuilder("from ").append(AppZoneSlot.class.getName())//
				.append(" where appId = :appId and cluster = :cluster")//
				.append(" and env = :env and appInstId = '' order by iNo");
		Query<AppZoneSlot> q = dao.createQuery(AppZoneSlot.class, sb.toString());
		q.setParameter("appId", o.getAppId());
		q.setParameter("cluster", o.getCluster());
		q.setParameter("env", o.getEnv());
		q.setMaxResults(1);
		List<AppZoneSlot> lt = q.getResultList();
		if (CollectionUtils.isEmpty(lt)) {
			return 0;
		}
		AppZoneSlot azs = lt.get(0);
		o.setZone(azs.getZone());
		dao.save(o);
		sb.setLength(0);
		sb.append("update ").append(AppZoneSlot.class.getName())//
				.append(" set appInstId = '").append(o.getId()).append("' where id = '")//
				.append(azs.getId()).append("' and appInstId = ''");
		if (dao.executeUpdate(sb.toString()) == 1) {
			return 1;
		}
		dao.delete(o);
		return -1;
	}

	@Override
	@Transactional
	public void update(AppInst o) {
		StringBuilder sb = new StringBuilder("from ").append(AppZoneSlot.class.getName())//
				.append(" where appId = :appId and cluster = :cluster")//
				.append(" and env = :env and appInstId = '' order by iNo");
		Query<AppZoneSlot> q = dao.createQuery(AppZoneSlot.class, sb.toString());
		q.setParameter("appId", o.getAppId());
		q.setParameter("cluster", o.getCluster());
		q.setParameter("env", o.getEnv());
		q.setMaxResults(1);
		List<AppZoneSlot> lt = q.getResultList();
		if (CollectionUtils.isEmpty(lt)) {
			return;
		}
		AppZoneSlot azs = lt.get(0);
		o.setZone(azs.getZone());
		dao.merge(o);
		sb.setLength(0);
		sb.append("update ").append(AppZoneSlot.class.getName())//
				.append(" set appInstId = '").append(o.getId()).append("' where id = '")//
				.append(azs.getId()).append("' and appInstId = ''");
		dao.executeUpdate(sb.toString());
	}

	@Override
	@Transactional
	public void save(AppInst o) {
		if (Util.strIsEmpty(o.getId())) {
			dao.save(o);
		} else {
			dao.merge(o);
		}
	}

	@Override
	@Transactional
	public void bind(String appId, String[] serverIds) {
		AppInfo a = dao.get(AppInfo.class, appId);
		String hql = new StringBuilder("select o from ").append(AppInst.class.getName())
				.append(" o where o.serverId=:serverId and o.appId=:appId and o.port=:port").toString();
		Query<AppInst> q = dao.createQuery(AppInst.class, hql);
		q.setParameter("appId", appId);
		q.setParameter("port", a.getPort());
		for (String serverId : serverIds) {
			q.setParameter("serverId", serverId); //
			AppInst o = (AppInst) q.uniqueResult();
			ServerInfo s = serverBiz.get(serverId);
			if (o == null) {
				o = new AppInst();
				o.setAppId(appId);
				o.setServerId(serverId);
				o.setPort(a.getPort());
				o.setIp(s.getIp());
				o.setEnv(s.getEnv());
			}
			o.setDeployPath(a.getDeployPath());
			o.setTempPath(a.getTempPath());
			o.setUser(a.getUser());
			dao.save(o);
		}
	}

	@Override
	@Transactional
	public void add(String[] appIds, String serverId) {
		String hql = new StringBuilder("select o from ").append(AppInst.class.getName())
				.append(" o where o.serverId=:serverId and o.appId=:appId and o.port=:port").toString();
		Query<AppInst> q = dao.createQuery(AppInst.class, hql);
		q.setParameter("serverId", serverId); //
		ServerInfo s = serverBiz.get(serverId);
		for (String appId : appIds) {
			AppInfo a = dao.get(AppInfo.class, appId);
			q.setParameter("appId", appId);
			q.setParameter("port", a.getPort());
			AppInst o = (AppInst) q.uniqueResult();
			if (o == null) {
				o = new AppInst();
				o.setAppId(appId);
				o.setServerId(serverId);
				o.setPort(a.getPort());
			}
			o.setIp(s.getIp());
			o.setEnv(s.getEnv());
			o.setDeployPath(a.getDeployPath());
			o.setTempPath(a.getTempPath());
			o.setUser(a.getUser());
			dao.save(o);
		}
	}

	@Override
	@Transactional
	public void delete(String id) {
		dao.delete(AppInst.class, id);
	}

	@Override
	@Transactional(readOnly = true)
	public AppInst get(String id) {
		AppInst o = dao.get(AppInst.class, id);
		dao.evict(o);
		return o;
	}

	@Override
	@Transactional(readOnly = true)
	public AppInst find(String appId, String serverId) {
		StringBuilder sb = new StringBuilder();
		sb.append(" and appId='").append(appId).append("'");
		sb.append(" and serverId='").append(serverId).append("'");
		return dao.find(AppInst.class, sb.toString());
	}

	@Override
	@Transactional
	public boolean status(String id, short status) {
		AppInst o = (AppInst) dao.get(AppInst.class, id);
		o.setStatus(status);
		return true;
	}

	@Override
	@Transactional
	public boolean copy(String oldAppId, String newAppId) {
		List<AppInst> lt = this.list(null, oldAppId, null, null);
		for (AppInst a : lt) {
			AppInst ai = new AppInst();
			ai.setAppId(newAppId);
			ai.setIp(a.getIp());
			ai.setDeployPath(a.getDeployPath());
			ai.setTempPath(a.getTempPath());
			ai.setPort(a.getPort());
			ai.setServerId(a.getServerId());
			ai.setStatus(a.getStatus());
			ai.setEnv(a.getEnv());
			this.add(ai);
		}
		return true;
	}

	/**
	 * 根据appId查询其未关联（未部署）的服务器列表信息 此方法正确性待校验，暂不使用
	 */
	@Transactional(readOnly = true)
	private PagedResult<ServerInfo> listServersNotAppId(String appId, String env, String orderBy, int pageNo, int pageSize) {
		StringBuilder sb = new StringBuilder();
		sb.append(" and a.id!='").append(appId).append("'");

		if (Util.strIsNotEmpty(env)) {// 部署环境
			sb.append(" and (l.env like '%").append(env);
			sb.append("%')");
		}
		String wl = new StringBuilder().append("select count(l) from ").append(AppInfo.class.getName())
				.append(" a join a.insts l").append(" where 1=1 ").append(sb).toString();
		int total = dao.getTotal(wl);
		if (Util.strIsNotEmpty(orderBy)) {
			String ob = Util.array2str(("l." + orderBy).replaceAll(",", ",").split(","), ",");
			sb.append(" order by ").append(ob);
		} else {// 没有排序条件就默认按name排序
			sb.append(" order by l.name");
		}
		String ql = new StringBuilder().append("select l from ").append(AppInfo.class.getName()).append(" a join a.insts l ")
				.append(" where 1=1 ").append(sb).toString();
		List<ServerInfo> lt = dao.list(ql, pageNo, pageSize);
		return new PagedResult<ServerInfo>(total, lt, pageNo, pageSize);
	}

	/**
	 * 根据appId查询其未关联（未部署）的服务器列表信息
	 */
	@Transactional(readOnly = true)
	private PagedResult<ServerInfo> listServersWhereAppNotOnIt(String appId, String projId, String env, String key,
			String orderBy, int pageNo, int pageSize) {
		StringBuilder sb = new StringBuilder();
		sb.append("select distinct s from ").append(ServerInfo.class.getName()).append(" s,").append(AppInfo.class.getName())
				.append(" a where 1=1 ");
		if ("".equals(env) || null == env || "ALL".equals(env)) {
			// 没有传环境参数或传递的环境参数为ALL，则查询所有环境，无需组装where env=条件
			log.info(JSON.toJSONString("前端请求没有传递env参数或传递的env参数值为‘ALL’."));
		} else if (!"ALL".equals(env) && !"".equals(env) && null != env) {
			sb.append("and s.env='").append(env).append("'");
		}

		if (Util.strIsNotEmpty(projId)) {// projId
			sb.append(" and s.projId='").append(projId).append("' ");
		}

		if (Util.strIsNotEmpty(key)) {// 部署环境
			sb.append(" and (s.code like '%").append(key);
			sb.append("%' or s.name like '%").append(key);
			sb.append("%' or s.memo like '%").append(key);
			sb.append("%') ");
		}

		sb.append(" and s not in elements(a.insts) and a.id='").append(appId).append("'").toString();
		StringBuilder sbs = new StringBuilder();
		sbs = sb;
		int total = dao.getTotal(sbs.toString().replace("distinct s", "count(s.id)"));
		if (Util.strIsNotEmpty(orderBy)) {
			String ob = Util.array2str(("s." + orderBy).replaceAll(",", ",").split(","), ",");
			sb.append(" order by ").append(ob);
		} else {// 没有排序条件就默认按name排序
			sb.append(" order by s.name");
		}

		List<ServerInfo> lt = dao.list(sb.toString(), pageNo, pageSize);
		return new PagedResult<ServerInfo>(total, lt, pageNo, pageSize);
	}

	@Override
	@Transactional
	public void delete(AppInst o) {
		dao.delete(o);
	}

	@Override
	@Transactional(readOnly = true)
	public AppInst find(String ip, Integer port) {
		StringBuilder sb = new StringBuilder();
		if (Util.strIsNotEmpty(ip)) {
			sb.append(" and ip='").append(ip).append("'");
		}
		if (port != null) {
			sb.append(" and port=").append(port);
		}
		return (AppInst) dao.find(AppInst.class, sb.toString());
	}

	@Override
	@Transactional(readOnly = true)
	public List<AppInst> list(String ip) {
		StringBuilder sb = new StringBuilder();
		sb.append(" and ip='").append(ip)//
			.append("' and status<>").append(AppInst.STATUS_DISABLED);
		List<AppInst> lt = dao.list(AppInst.class, sb.toString());
		for (AppInst o : lt) {
			dao.evict(o);
		}
		return lt;
	}
}